ES6有6种简单数据类型(原始数据类型):
和1种复杂数据类型:
可以通过 typeof 操作符查看任意变量是何种数据类型,会返回以下字符串之一:
let message = "some string";
console.log(typeof message); // "string",通过操作符获取类型结果
console.log(typeof(message)); // "string",通过参数的方式获取结果
console.log(typeof 95); // "number"
Undefined 类型只有一个值,就是特殊值 undefined。当使用 var 或 let 声明了变量但没有初始化时,就相当于给变量赋予了 undefined 值
对未初始化的变量调用 typeof 时,返回的结果是"undefined",对未声明的变量调用它时, 返回的结果还是"undefined"
let message; // 这个变量被声明了,只是值为undefined
// let age // 确保没有声明过这个变量
console.log(typeof message); // "undefined"
console.log(typeof age); // "undefined"
// 这在逻辑上讲是正确的
// 虽然严格来说这两个变量存在根本性差异,但它们都无法执行实际操作
Null 类型同样只有一个值,即特殊值 null,它表示一个空对象指针,因此调用 typeof 时会返回 object
let car = null;
console.log(typeof car); // "object"
在当前定义一个变量需要未来对它进行赋值的情况下,建议使用 null 进行初始化,这样只要检查这个变量是不是 null 就可知道该变量是否在后来重新进行了赋值
if (car != null) {
// car 是一个对象的引用
}
undefined 值是由 null 值派生而来的,因此表面上是相等的
console.log(null == undefined); // true
console.log(null === undefined); // false
小结1:
Boolean(布尔值)类型有两个字面值:true 和 false,这两个值不同于数值,因此 true 不等于 1,false 不等于 0。虽然布尔值只有两个,但所有其他类型的值都有相应布尔值的等价形式,可以调用 Boolean() 进行转换:
let message = "Hello world!";
let messageAsBoolean = Boolean(message);
什么值能转换为 true 或 false 的规则取决于数据类型和实际的值:
数据类型 | 转换为true | 转换为false |
---|---|---|
Boolean | true | false |
String | 非空字符串 | ""(空字符串) |
Number | 非零数值(包括无穷值) | 0、NaN |
Object | 任意对象 | null |
Undefined | N/A(不存在) | undefined |
if 语句会自动将其他类型转换到Boolean类型
let message = "Hello world!";
if (message) {
console.log("Value is true");
}
// 十进制整数
let intNum = 55;
// 八进制整数,第一个数字必须为0,其余数字为0~7
let octalNum1 = 070; // 八进制的56
let octalNum2 = 079; // 无效的八进制数,忽略前缀0直接当成79处理
let octalNum3 = 0o70; // 严格模式下以0o前缀表示八进制数
// 十六进制整数,以0x前缀表示
let hexNum1 = 0xA; // 十六进制10
let hexNum2 = 0x1f; // 十六进制31
// 浮点数
let floatNum1 = 1.1;
let floatNum2 = 0.1;
let floatNum3 = .1; // 可以忽略开头的0,不推荐
// 因为存储浮点值使用的内存空间是存储整数值的两倍,所以会尽量将浮点数转换为整数
let floatNum4 = 1.; // 小数点后面没有数字,当成整数 1 处理
let floatNum5 = 10.0; // 小数点后面是零,当成整数 10 处理
// 使用科学计数法处理非常大或非常小的值
// 一个数值(整数或浮点数)后跟一个大写或小写的字母e,再加上一个要乘的10的多少次幂
let floatNum6 = 3.125e7; // 等于31250000
let floatNum7 = 3e-7; // 等于0.0000003
Number.MIN_VALUE 表示JS的最小数值,Number.MAX_VALUE 表示JS的最大数值,超过这个范围的数被表示为 +Infinity 或者 -Infinity
要确定一个值是不是有限大(即介于 JS 能表示的最小值和最大值之间),可以使用 isFinite() 函数
let result = Number.MAX_VALUE + Number.MAX_VALUE;
console.log(isFinite(result)); // false
有一个特殊的数值叫 NaN,意思是“不是数值”(Not a Number),用于表示本来要返回数值的操作失败了
console.log(0 / 0); // NaN
console.log(1 / 0); // Infinity
console.log(-1 / 0); // -Infinity
小结2:
任何涉及 NaN 的操作始终返回 NaN(如 NaN / 10)
NaN 不等于包括 NaN 在内的任何值
console.log(NaN == NaN); // false
通过 isNaN() 函数做判断,判断时 JS 会尝试把参数转换为数值再进行传递
console.log(isNaN(NaN)); // true
console.log(isNaN(10)); // false,10是数值
console.log(isNaN("10")); // false,可以转换为数值10
console.log(isNaN("blue")); // true,不可以转换为数值
console.log(isNaN(true)); // false,可以转换为数值1
有 3 个函数可以将非数值转换为数值:
转换的规则表述起来比较麻烦,感兴趣建议看书上原文。日常应用中除非做面试题,否则一般也用不到,现实工作往往通过相互约定、代码规范或者控制台输出校验来处理。
字符串可以使用双引号(")、 单引号(')或反引号(`)标示
let firstName = "John";
let lastName = 'Jacob';
let lastName = `Jingleheimerschmidt`;
let text = "This is the letter sigma: \u03a3."; // 可以包含转义字符
console.log(text.length); // 通过 length 方法获取字符串长度 28
字符串是不可变的(immutable),一旦创建,它的值就不能变了
有两种方式把一个值转换为字符串:
toString() 方法
let age = 11;
let ageAsString = age.toString(); // 字符串"11"
let found = true;
let foundAsString = found.toString(); // 字符串"true"
null 和 undefined 值没有 toString() 方法
toString()可以接收一个底数参数,即以什么底数来输出数值的字符串表示,这在输出特定进制数字的情况下很方便
let num = 10;
console.log(num.toString()); // "10"
console.log(num.toString(2)); // "1010"
console.log(num.toString(8)); // "12"
console.log(num.toString(10)); // "10"
console.log(num.toString(16)); // "a"
String()转型函数
let value1 = 10;
let value2 = true;
let value3 = null;
let value4;
// 如果值有 toString() 方法,则调用该方法(不传参数)并返回结果
console.log(String(value1)); // "10"
console.log(String(value2)); // "true"
// 如果值是 null,返回"null"
console.log(String(value3)); // "null"
// 如果值是 undefined,返回"undefined"
console.log(String(value4)); // "undefined"
ES6使用反引号(`)定义的字符串可以保留换行字符
// 分别通过转义字符和模板字面量定义字符串
let myMultiLineString = 'first line\nsecond line';
let myMultiLineTemplateLiteral = `first line
second line`;
console.log(myMultiLineString);
// first line
// second line
console.log(myMultiLineTemplateLiteral);
// first line
// second line
// 两种定义方法得到的字符串是一致的
console.log(myMultiLineString === myMultiLinetemplateLiteral); // true
一般用来定义HTML模板
let pageHTML = `
<div>
<a href="#">
<span>Jake</span>
</a>
</div>`;
由于模板字面量会保持反引号内部的空格和换行,所以往往在书写时会因为缩进问题显得格式会怪怪的
// 这个模板字面量虽然看上去整齐,但是实际却包含很多空格
let myTemplateLiteral = `first line
second line`;
console.log(myTemplateLiteral.length); // 不一定是多少
// 这个模板字面量以一个换行符开头
let secondTemplateLiteral = `
first line
second line`;
console.log(secondTemplateLiteral[0] === '\n'); // true
// 这个模板字面量没有意料之外的字符,但是编写起来简直是强迫症的天敌
let thirdTemplateLiteral = `first line
second line`;
console.log(thirdTemplateLiteral);
// first line
// second line
模板字面量中的插值语法:通过在 ${} 中使用一个 JS 表达式实现
let value = 5;
let exponent = 'second';
// 以前,字符串插值是这样实现的:
let str1 = value + ' to the ' + exponent + ' power is ' + (value * value);
// 现在,可以用模板字面量这样实现:
let str2 = `${ value } to the ${ exponent } power is ${ value * value }`;
console.log(str1);
console.log(str2);
模板字面量支持标签函数,标签函数会接收被插值记号分隔后的模板和对每个表达式求值的结果
let a = 6;
let b = 9;
function simpleTag(strings, aValExpression, bValExpression, sumExpression) {
console.log(strings);
console.log(aValExpression);
console.log(bValExpression);
console.log(sumExpression);
return 'foobar';
}
let untaggedResult = `${ a } + ${ b } = ${ a + b }`;
let taggedResult = simpleTag`${ a } + ${ b } = ${ a + b }`;
// ["", " + ", " = ", ""]
// 6
// 9
// 15
console.log(untaggedResult);
console.log(taggedResult);
// "6 + 9 = 15"
// "foobar"
通过 String.raw 函数获取模板字面量的原始内容
// Unicode 示例
// \u00A9 是版权符号
console.log(`\u00A9`); // ©
console.log(String.raw`\u00A9`); // \u00A9
// 换行符示例
console.log(`first line\nsecond line`);
// first line
// second line
console.log(String.raw`first line\nsecond line`);
// "first line\nsecond line"
// 对实际的换行符来说是不行的
// 因为它们不会被转换成转义序列的形式
console.log(String.raw`first line
second line`);
// first line
// second line
Symbol 是 ES 6 新增的数据类型,用来创建唯一记号,进而用作非字符串形式的对象属性
符号需要使用 Symbol() 函数初始化,因为符号本身是原始类型,所以 typeof 操作符对符号返回 symbol
let sym = Symbol();
console.log(typeof sym); // symbol
可以传入一个字符串参数作为对符号的描述,将来可以通过这个字符串来调试代码
let genericSymbol = Symbol();
let otherGenericSymbol = Symbol();
let fooSymbol = Symbol('foo');
let otherFooSymbol = Symbol('foo');
console.log(genericSymbol == otherGenericSymbol); // false
console.log(fooSymbol == otherFooSymbol); // false
与其他类型不同的是,Symbol() 函数不能与 new 关键字一起作为构造函数使用
let myBoolean = new Boolean();
console.log(typeof myBoolean); // "object"
let myString = new String();
console.log(typeof myString); // "object"
let myNumber = new Number();
console.log(typeof myNumber); // "object"
let mySymbol = new Symbol(); // TypeError: Symbol is not a constructor
如果运行时的不同部分需要共享和重用符号实例,那么可以用一个字符串作为键,在全局符号注册表中创建并重用符号。为此,需要使用 Symbol.for() 方法:
let fooGlobalSymbol = Symbol.for('foo');
console.log(typeof fooGlobalSymbol); // symbol
let otherFooGlobalSymbol = Symbol.for('foo'); // 重用已有符号
console.log(fooGlobalSymbol === otherFooGlobalSymbol); // true
可以使用 Symbol.keyFor() 来查询全局注册表,这个方法接收符号,返回该全局符号对应的字符串键,如果查询的不是全局符号,则返回 undefined
// 创建全局符号
let s = Symbol.for('foo');
console.log(Symbol.keyFor(s)); // foo
// 创建普通符号
let s2 = Symbol('bar');
console.log(Symbol.keyFor(s2)); // undefined
// 如果传给Symbol.keyFor()的不是符号,则该方法抛出 TypeError
Symbol.keyFor(123); // TypeError: 123 is not a symbol
凡是可以使用字符串或数值作为属性的地方,都可以使用符号
let s1 = Symbol('foo'),
s2 = Symbol('bar'),
s3 = Symbol('baz'),
s4 = Symbol('qux');
let o = {
[s1]: 'foo val'
};
// 这样也可以 o[s1] = 'foo val';
console.log(o); // {Symbol(foo): foo val}
Object.defineProperty(o, s2, {value: 'bar val'});
console.log(o); // {Symbol(foo): foo val, Symbol(bar): bar val}
Object.defineProperties(o, {
[s3]: {value: 'baz val'},
[s4]: {value: 'qux val'}
});
console.log(o);
// {Symbol(foo): foo val, Symbol(bar): bar val,
// Symbol(baz): baz val, Symbol(qux): qux val}
类似于 Object.getOwnPropertyNames() 返回对象实例的常规属性数组,Object.getOwnPropertySymbols() 返回对象实例的符号属性数组。这两个方法的返回值彼此互斥。Object.getOwnPropertyDescriptors() 会返回同时包含常规和符号属性描述符的对象。Reflect.ownKeys()会返回两种类型的键:
let s1 = Symbol('foo'), s2 = Symbol('bar');
let o = {
[s1]: 'foo val',
[s2]: 'bar val',
baz: 'baz val',
qux: 'qux val'
};
console.log(Object.getOwnPropertySymbols(o));
// [Symbol(foo), Symbol(bar)]
console.log(Object.getOwnPropertyNames(o));
// ["baz", "qux"]
console.log(Object.getOwnPropertyDescriptors(o));
// {baz: {...}, qux: {...}, Symbol(foo): {...}, Symbol(bar): {...}}
console.log(Reflect.ownKeys(o));
// ["baz", "qux", Symbol(foo), Symbol(bar)]
小结3:
与其他类型相比,Symbol类型并不常用,但是有些框架或者第三方库中会用到,需要达到看到能认识的程度,等真正遇到了再进一步了解其详细用法即可
Object(对象)是一组数据和功能的集合,通过 new 操作符后跟对象类型的名称来创建。开发者可以通过创建 Object 类型的实例来创建自己的对象,然后再给对象添加属性和方法:
let o1 = new Object();
let o2 = new Object; // 没有参数可省略括号,但不推荐
每个 Object 实例都有如下属性和方法:
constructor,用于创建当前对象的函数
hasOwnProperty(propertyName),用于判断当前对象实例上是否存在给定的属性
let o = new Object();
// 要检查的属性名必须是字符串或符号
let flag = o.hasOwnProperty("name");
isPrototypeOf(object),用于判断当前对象是否为另一个对象的原型
propertyIsEnumerable(propertyName),用于判断给定的属性是否可枚举,与 hasOwnProperty() 一样,属性名必须是字符串
toLocaleString(),返回对象的字符串表示
toString(),返回对象的字符串表示
valueOf(),返回对象对应的字符串、数值或布尔值表示,通常与 toString() 的返回值相同